package com.xindacloud.sms.adapter.controller;

import com.google.gson.Gson;
import com.xindacloud.sms.adapter.bo.CheckPhoneNumberBO;
import com.xindacloud.sms.adapter.bo.HuoxinReportDataBO;
import com.xindacloud.sms.adapter.bo.PushYYDlrBO;
import com.xindacloud.sms.adapter.component.ReceiveDLRConvertComponent;
import com.xindacloud.sms.adapter.constant.CommonConstant;
import com.xindacloud.sms.adapter.dto.PushYYDlrDTO;
import com.xindacloud.sms.adapter.entity.SmsDeliveryReceipt;
import com.xindacloud.sms.adapter.entity.SmsSubmissionRecord;
import com.xindacloud.sms.adapter.entity.SmsUser;
import com.xindacloud.sms.adapter.entity.SmsUserAllowOperator;
import com.xindacloud.sms.adapter.service.business.ISmsDeliveryReceiptService;
import com.xindacloud.sms.adapter.service.business.ISmsSubmissionRecordService;
import com.xindacloud.sms.adapter.service.business.ISmsUserAllowOperatorService;
import com.xindacloud.sms.adapter.service.business.ISmsUserService;
import com.xindacloud.sms.adapter.utils.*;
import com.xindacloud.sms.adapter.vo.PushYYDlrVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Api(tags = {"短信交货报告接口模块"})
@Slf4j
@RestController
@RequestMapping(value = "/api/v1/adapter/sms/dlr")
public class ReceiveDLRController {

    final ISmsDeliveryReceiptService deliveryReceiptService;

    @Autowired
    ISmsSubmissionRecordService smsSubmissionRecordService;

    @Autowired
    private ISmsUserAllowOperatorService smsUserAllowOperatorService;
    @Autowired
    private ISmsUserService smsUserService;

    @Autowired
    ReceiveDLRConvertComponent receiveDLRConvertComponent;

    @Autowired
    private RestTemplate restTemplate;

    final Gson gson = new Gson();

    @Autowired
    public ReceiveDLRController(ISmsDeliveryReceiptService deliveryReceiptService) {
        this.deliveryReceiptService = deliveryReceiptService;
    }

    @ApiOperation(value = "Nexmo交货报告")
    @PostMapping("/webhooks/delivery-report")
    public UnionResultHelp nexmoDeliveryReceipt(@RequestBody SmsDeliveryReceipt deliveryReceipt) {
        try {

            @NotNull final SmsDeliveryReceipt nexmoReceiveDLRDTO = deliveryReceipt;
            log.info("===== Nexmo 回执报告参数:{}", gson.toJson(deliveryReceipt));
            boolean resultFlag = deliveryReceiptService.saveDeliveryReceipt(nexmoReceiveDLRDTO);
            log.info("save SmsDeliveryReceipt result:{}", resultFlag);
            final SmsSubmissionRecord build = getSmsSubmissionRecord(nexmoReceiveDLRDTO);
            smsSubmissionRecordService.updateStatusForDLR(build);
            pushDLRYY(nexmoReceiveDLRDTO.getMessageId(), nexmoReceiveDLRDTO.getSentTo());
            return combinedUnionResult(UnionResultHelp.StatusEnum.Success.code(), "success");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
        }
    }

    @ApiOperation(value = "Sinch交货报告")
    @PostMapping(path = "/sinch/delivery-report")
    public UnionResultHelp sinchDeliveryReceipt(@RequestBody SmsDeliveryReceipt deliveryReceipt) {
        try {
            log.info("===== Sinch 回执报告参数:{}", gson.toJson(deliveryReceipt));
            deliveryReceiptService.saveDeliveryReceipt(deliveryReceipt);
            final SmsSubmissionRecord build = getSmsSubmissionRecord(deliveryReceipt);
            smsSubmissionRecordService.updateStatusForDLR(build);
            pushDLRYY(deliveryReceipt.getMessageId(), deliveryReceipt.getSentTo());
            log.info("Sinch update status.");
            return combinedUnionResult(UnionResultHelp.StatusEnum.Success.code(), "success");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
        }
    }

    @ApiOperation(value = "CM交货报告")
    @PostMapping("/cm/delivery-report")
    public UnionResultHelp cmDeliveryReceipt(@RequestBody SmsDeliveryReceipt deliveryReceipt) {

        try {
            @NotNull final SmsDeliveryReceipt cmReceiveDLRDTO = deliveryReceipt;
            log.info("===== CM 回执报告参数:{}", gson.toJson(cmReceiveDLRDTO));
            deliveryReceiptService.saveDeliveryReceipt(cmReceiveDLRDTO);
            final SmsSubmissionRecord build = getSmsSubmissionRecord(cmReceiveDLRDTO);
            if (Objects.equals(cmReceiveDLRDTO.getErrorCode(), "0")
                    || Objects.equals(cmReceiveDLRDTO.getErrorCode(), "2")) {
                build.setErrorText("");
            }
            final String network = cmReceiveDLRDTO.getNetworkCode();
            final SmsSubmissionRecord submissionRecord = smsSubmissionRecordService
                    .queryOneSubmissionRecordFor(build.getSendTo(), build.getMessageId());
            if (Objects.isNull(submissionRecord)) {
                log.warn("###根据发送手机号:{},消息ID:{},查询短信提交记录为空.", build.getSendTo(), build.getMessageId());
                return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
            }
            if (!submissionRecord.isMinus() && StringUtils.isNotEmpty(network)) {
                //检查号码所属国家
                final CheckPhoneNumberBO phoneNumberBO = PhoneUtil.getPoneNumber(build.getSendTo());
                final String countryCode = phoneNumberBO.getRegion();
                final String countryPrefix = String.valueOf(phoneNumberBO.getCountryPrefix());
                final Integer userId = submissionRecord.getUserId();
                final Integer messageCount = submissionRecord.getMessageCount();
                log.info("根据网络号:{}扣费", network);
                final SmsUserAllowOperator userAllowOperator = smsUserAllowOperatorService
                        .queryOneMaxPriceOrderByDesc(userId, countryCode, countryPrefix, network);
                final BigDecimal price = userAllowOperator.getPrice();
                //final String agentId = userAllowOperator.getAgentId();
                final BigDecimal calcMessagePrice = MessageTypeHelp.calcMessagePrice(price, messageCount);
                smsUserService.updateSmsPrice(userId, calcMessagePrice);
                build.setFree(price);
                //build.setAgentId(agentId);
            }
            smsSubmissionRecordService.updateStatusForDLR(build);

            pushDLRYY(cmReceiveDLRDTO.getMessageId(), cmReceiveDLRDTO.getSentTo());
            return combinedUnionResult(UnionResultHelp.StatusEnum.Success.code(), "success");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
        }
    }


    @ApiOperation(value = "国内交货报告")
    @PostMapping("/domestic/delivery-report")
    public UnionResultHelp domesticDeliveryReceipt(@RequestBody SmsDeliveryReceipt deliveryReceipt) {

        try {
            @NotNull final SmsDeliveryReceipt domesticReceiveDLRDTO = deliveryReceipt;
            log.info("===== 国内 回执报告参数:{}", gson.toJson(domesticReceiveDLRDTO));
            deliveryReceiptService.saveDeliveryReceipt(domesticReceiveDLRDTO);
            smsSubmissionRecordService.updateStatusForDLR(getSmsSubmissionRecord(domesticReceiveDLRDTO));

            pushDLRYY(domesticReceiveDLRDTO.getMessageId(), domesticReceiveDLRDTO.getSentTo());
            return combinedUnionResult(UnionResultHelp.StatusEnum.Success.code(), "success");
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
        }
    }


    @PostMapping(value = "/huoxin/delivery-report")
    public UnionResultHelp getHuoxinDeliveryReceipt(@RequestBody SmsDeliveryReceipt deliveryReceipt) {
        try {
            @NotNull final SmsDeliveryReceipt huoxinReceiveDLRDTO = deliveryReceipt;
            log.info("!!!!! 火信云推送报告：messageId:{},号码为:{}", huoxinReceiveDLRDTO.getMessageId(), huoxinReceiveDLRDTO.getSentTo());
            log.info("!!!!! 火信云 回执报告参数:{}", gson.toJson(huoxinReceiveDLRDTO));
            deliveryReceiptService.saveDeliveryReceipt(huoxinReceiveDLRDTO);
            smsSubmissionRecordService.updateStatusForDLR(getSmsSubmissionRecord(huoxinReceiveDLRDTO));
            return combinedUnionResult(UnionResultHelp.StatusEnum.Success.code(), "success");
        }catch(Exception e){
            log.error(e.getMessage(), e);
            return combinedUnionResult(UnionResultHelp.StatusEnum.Fail.code(), "fail");
        }
    }


    @PostMapping(value = "/yy/test", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
    public Object test_yy(PushYYDlrVO pushYYDlrVO, HttpServletRequest request) {
        log.info("content-type: {}", request.getHeader("content-type"));
        log.info("account: {}", pushYYDlrVO.getAccount());
        log.info("data: {}", pushYYDlrVO.getData());
        final Gson gson = new Gson();
        List<PushYYDlrDTO> list = gson.fromJson(pushYYDlrVO.getData(), List.class);
        log.info(gson.toJson(list));
        log.info("time: {}", pushYYDlrVO.getTime());
        log.info("sign: {}", pushYYDlrVO.getSign());

        Map<Object, Object> map = new HashMap<>();
        map.put("code", 0);
        map.put("msg", "success");
        return map;
    }

    private SmsSubmissionRecord getSmsSubmissionRecord(SmsDeliveryReceipt deliveryReceipt) {
        return SmsSubmissionRecord.builder()
                .messageId(deliveryReceipt.getMessageId())
                .sendTo(deliveryReceipt.getSentTo())
                .network(deliveryReceipt.getNetworkCode())
                .finalStatus(deliveryReceipt.getStatus().toLowerCase())
                .errorText(deliveryReceipt.getReason())
                .build();
    }


    private void pushDLRYY(String messageId, String phoneNumber) {
        final SmsSubmissionRecord smsSubmissionRecord = getSmsSubmissionRecordFor(messageId, phoneNumber);
        final String cbkUrl = smsSubmissionRecord.getCbkUrl();
//        final String cbkUrl = "http://127.0.0.1:8080/api/yy/dlr/test";

        if (StringUtils.isNotBlank(cbkUrl)) {
            // 根据messageId, phoneNumber查询taskId, 以及该taskId下的所有短信回执情况
            final String taskId = smsSubmissionRecord.getTaskId();
            List<SmsSubmissionRecord> smsSubmissionRecords = smsSubmissionRecordService.querySmsSubmissionRecordByTaskId(taskId);

            // 回执消息格式转换
            List<PushYYDlrDTO> pushYYDlrDTOS = convertSmsSubmissionRecord(smsSubmissionRecords);
            List<List<PushYYDlrDTO>> pushRecords = splitList(pushYYDlrDTOS, CommonConstant.YY_GROUP_SIZE);

            // 获取推送必要信息
            SmsUser smsUser = smsUserService.queryUserInfoById(smsSubmissionRecord.getUserId());
            final String accountName = smsUser.getAccountName();
            final String password = smsUser.getTextPassword();

            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

            // 调用cbkURL推送到YY, 如果记录超过100条, 分批推送
            for (List<PushYYDlrDTO> pushRecord : pushRecords) {
                MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
                map.add("account", accountName);

                String data = new Gson().toJson(pushRecord);
                map.add("data", data);

                long time = System.currentTimeMillis();
                map.add("time", time);

                String sign = HMACMD5Util.encryption(accountName + data + String.valueOf(time) + password);
                map.add("sign", sign);

                HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<MultiValueMap<String, Object>>(map, headers);
                log.info("======推送YY参数: {}", gson.toJson(map));
                ResponseEntity<Object> response = restTemplate.postForEntity(cbkUrl, httpEntity, Object.class);
                log.info("YY返回结果: {}", response.toString());
                log.info("======推送YY回执结果: {}", gson.toJson(response.getBody()));
            }
        }

    }


    /**
     * 根据messageId跟phoneNumber查询出该条短信记录信息
     *
     * @param messageId
     * @param phoneNumber
     * @return
     */
    private SmsSubmissionRecord getSmsSubmissionRecordFor(String messageId, String phoneNumber) {
        SmsSubmissionRecord smsSubmissionRecord = smsSubmissionRecordService.queryOneSubmissionRecordFor(phoneNumber, messageId);
        if (Objects.isNull(smsSubmissionRecord)) {
            smsSubmissionRecord = smsSubmissionRecordService.queryOneSubmissionRecordByMessageId(messageId);
        }
        return smsSubmissionRecord;
    }


    /**
     * 将回执记录格式转换为YY要求格式
     *
     * @param smsSubmissionRecords
     * @return
     */
    private List<PushYYDlrDTO> convertSmsSubmissionRecord(List<SmsSubmissionRecord> smsSubmissionRecords) {
        List<PushYYDlrDTO> pushYYDlrDTOS = new ArrayList<>();
        // 回执消息格式转换
        for (SmsSubmissionRecord record : smsSubmissionRecords) {
            pushYYDlrDTOS.add(PushYYDlrDTO.builder()
                            .msgid(record.getTaskId())
                            .mobile(record.getSendTo())
                            .send_time(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(record.getSubmitTime()))
                            .rpt_time(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").format(record.getCreateTime()))
//                    .rpt_state(getRptStateByStatus(record.getFinalStatus()))
                            .rpt_state(Objects.isNull(getRptStateByStatus(record.getFinalStatus())) ? getRptStateByStatus("failed") : getRptStateByStatus(record.getFinalStatus()))
                            .rpt_sub_state(record.getFinalStatus())
//                    .rpt_desc(getDescByStatus(record.getFinalStatus()))
                            .rpt_desc(Objects.isNull(getDescByStatus(record.getFinalStatus())) ? getDescByStatus("failed") : getDescByStatus(record.getFinalStatus()))
                            .build()
            );
        }
        return pushYYDlrDTOS;
    }


    /**
     * 根据回执状态细化码适配短信回执状态
     *
     * @param status
     * @return
     */
    private String getRptStateByStatus(String status) {
        for (RptStateEum rptStateEum : RptStateEum.values()) {
            if (status.equals(rptStateEum.getStatus())) {
                return String.valueOf(rptStateEum.getRptState());
            }
        }
        return null;
    }


    /**
     * 根据回执状态细化码适配回执消息详情描述
     *
     * @param status
     * @return
     */
    private String getDescByStatus(String status) {
        for (RptDescEum rptDescEum : RptDescEum.values()) {
            if (status.equals(rptDescEum.getStatus())) {
                return rptDescEum.getDesc();
            }
        }
        return null;
    }


    /**
     * 根据单次允许的最大消息数分批回执消息
     *
     * @param messagesList
     * @param groupSize
     * @return
     */
    private List<List<PushYYDlrDTO>> splitList(List<PushYYDlrDTO> messagesList, int groupSize) {
        int length = messagesList.size();
        // 计算可以分成多少组
        int num = (length + groupSize - 1) / groupSize;
        List<List<PushYYDlrDTO>> newList = new ArrayList<>(num);
        for (int i = 0; i < num; i++) {
            // 开始位置
            int fromIndex = i * groupSize;
            // 结束位置
            int toIndex = (i + 1) * groupSize < length ? (i + 1) * groupSize : length;
            newList.add(messagesList.subList(fromIndex, toIndex));
        }
        return newList;
    }

    private static UnionResultHelp combinedUnionResult(Integer code, String message) {
        final UnionResultHelp unionResultHelp = new UnionResultHelp();
        unionResultHelp.setStatus(code);
        unionResultHelp.setMessage(message);
        unionResultHelp.setTimestamp(UnionResultHelp.getStrTimestamp());
        return unionResultHelp;
    }
}
